<html>
    <head>
        <title>你的第一个组件</title>
    </head>
    <body>
        <script>
            /*
                 React 是一个用于渲染用户界面(UI)的JavaScript库。  

                 UI由 按钮、文本 和图片 等小单元构建而成。React 允许你将它们组合成"可重用"、"可嵌套"的组件。
                 从网站到手机应用，屏幕上的所有内容都可以分解为组件。
            */


                //一、你的第一个组件:
                      /*
                          React 应用是由称为"组件"的独立UI部分构建的。 
                          React组件是一个"JavaScript函数"，你可以在其中"添加标记"(jsx)
                          组件可以"小到一个按钮"，也可以"大到整个页面"。
                      */


                            function Profile(){
                                // 注意, 第一次使用 JSX
                                return (<img src="https://i.imgur.com/MKyGUdm.jpg" alt="Katherine Johnson" />);
                            }

                                // 默认导出 Gallery 组件(函数)
                            export default function Gallery(){  
                                return  (
                                    <section>
                                        <Profile/>
                                        <Profile/>
                                        <Profile/>
                                    </section>
                                );
                            }

                // 二、"导入"和"导出" 组件
                      /*
                            你可以在一个文件中声明多个组件，单大文件可能"难以导航"。
                            要解决这个问题，你可以将组件从它自己独立的文件中"导出"("默认导出"的方式),然后在要使用该组件的文件中"导入"
                      */
                           // 例如, 上面的例子在"模块化js"中可以这样写：
                                    // 在Profile.js中
                                        export default function Profile(){
                                            return (
                                                <img src="https://i.imgur.com/qir.jpg" alt="Alan L.Hart" />
                                            )
                                        }

                                    // 在Gallery.js ，因为要使用 Profile组件，所以需要导入它
                                        import Profile from './Profile.js';
                                        
                                        export default function Gallery(){
                                            return (
                                                <section>
                                                    <h1>Amazing scientists</h1>
                                                    <Profile/>
                                                    <Profile/>
                                                    <Profile/>
                                                </section>
                                            )
                                        }


                // 三、使用JSX 编写标记 (其实是编写组件输出的Html结构)
                     
                      /*
                            每个React组件都是一个JavaScript函数，其中可能包含React渲染到浏览器中的HTML。
                            React使用名叫"JSX"的语法扩来编辑这些html。
                            JSX看起来很像HTMl ，但更严格一些，并且可以"动态信息"。

                            如果，我们将现有的HTML代码粘贴到 React组件中，它并不总是有效。

                            如果，你有这样"现有的html代码"，你可以使用"转换器"修复它，让他能在React中使用。
                            
                      */

                      /*
                            JSX的模板语法(使用"{}"的JavaScript)：

                                 JSX允许你在JavaScript文件中编写类似HTML的标记，将"渲染逻辑"和"内容"写在同一位置。
                                 有时候你会希望在"标记语言内"添加一些"JavaScript逻辑"或"引用动态属性"。
                                 在这种情况下，你可以在JSX中使用大括号"{}",来给"标记"打开一扇通往JavaScript的创建。

                                 注： 这个有点类似Vue.js的模板语法，但是，在Vue.js中，我们使用的是"v-bind:style"来绑定样式。
                      */

                                 // eg: 
                                     const person={
                                        name:'余冰',
                                        theme:{
                                            backgrountdColor:'black',
                                            color:'pink'
                                        }
                                     }

                                     export default function TodoList(){
                                        return (
                                            <div style={person.theme}>
                                                <h1>{person.name}'s Todos</h1>
                                                <img
                                                    className="avatar"
                                                    src="https://i.imgur.com/7vQD0fPs.jpg"
                                                    alt="Gregorio Y. Zara"
                                                />
                                                <ul>
                                                    <li>Improve the videophone</li>
                                                    <li>Prepare aeronautics lectures</li>
                                                    <li>Work on the alcohol-fuelled engine</li>
                                                </ul>
                                            </div>
                                        );
                                     }

            // 四、将属性(porps)传递给组件
                        /*
                                React组件使用"props(属性)"进行"相互通信"。
                                每个父组件都可以"通过给子组件以属性"的方式，给子组件传递信息。 
                                "Props(属性)"可能让你想起HTML属性，但你可以通过它们"传递任何JavaScript值"，包括 对象、数组、函数 ，甚至是JSX
                        */

                        /*
                                Props(属性)是你传递给JSX标签的信息。例如 className、src、alt、width 和 height等，便是一些可以传递给<img>的props

                                你可以传递给<img>标签的props是预定义的(ReactDom复合html标准)。但是你可以进任何props传递给"你自己的组件"
                        */

                                // 1、步骤1：将props属性"传递给子组件"
                                       // 有个组件 utils.js   不返回JSX的函数，只是返回一个值,"使用时直接导入，直接调用即可"
                                           export function getIamgeUrl(person,size='s'){
                                                return (
                                                    'https:// i.imgur.com/' + person.imageId+size+'.jpg'
                                                );
                                           }

                                       // 在app.js 中
                                          import {getIamgeUrl} from './utils.js';

                                          export default function Profile(){
                                            return(
                                                <Card>
                                                    <Avatar size={100}
                                                            person={{
                                                                name:'Katherine Johnson',
                                                                imageId:'MKyGUdm'
                                                            }}
                                                    />
                                                </Card>
                                            );
                                          }

                                          // 在react中，范式返回JSX的函数，都是"组件" 都能用 <></> 包裹起来,在"别的组件"中使用它们
                                                          // 解构赋值
                                          function Avatar ({person,size}){   // 这里的参数({person,size}) 是ES6的解构赋值语法,用来从对象中提取对应名称的属性，并直接作为函数参数来用

                                            return(
                                                <img 
                                                    className="avatar"
                                                    src={getIamgeUrl(person)}
                                                    alt={person.name}
                                                    width={size}
                                                    height={size}
                                                />
                                            );
                                          }

                                          // 在react中，范式返回JSX的函数，都是"组件" 都能用 <></> 包裹起来,在"别的组件"中使用它们
                                                        //解构赋值
                                          function Card({children}){ // 这里的参数({person,size}) 是ES6的解构赋值语法,用来从对象中提取对应名称的属性，并直接作为函数参数来用
                                                                     // children 是一个特殊的属性，它包含了组件标签之间的内容。
                                            return (
                                                <div className="card">
                                                    {children}
                                                </div>
                                            );
                                          }

                                // 2、步骤2:读取子组件内的props（属性）
                                         /*
                                             你可以直接在 functin Avatar 之后在 "({ })" 中列出他们的名称 person ,size ,并用逗号分隔来读取这些属性。
                                             
                                             （这其实是ES6的解构赋值语法，用来从对象中提取对应名称的属性，并直接作为函数参数来用）。

                                             在React内部，其实给是 function Avatar 传递了一个对象，这个对象包含了所有传递给组件的属性。(props对象)
                                         */
                                         function Avatar({ person, size }) {
                                            // 然后使用 person 和 Size这两个熟悉，进行一些逻辑性渲染就行了。
                                         }

                                        /*
                                            属性，让你独立地考虑父组件和子组件。
                                            例如，你可以在profile中更改 person或者Size属性，而不必考虑Avatar如何使用它们。
                                            同样，你更改Avatar使用这些属性的方式，而不需要查看Profile.js文件。
                                        */

                                        /*
                                            事实上，props是组件的"唯一参数"！  React组件函数只接受一个参数，一个porps对象！

                                            所以也可以像这样写:
                                        */
                                            function Avatar(props){
                                                let person = props.person;
                                                let size =person.size;
                                            }
                                            // 通常你不需要整个 props对象本身，所以你将它"解构赋值"为"单独的属性"

                                // 3、为属性指定默认值
                                         /*
                                            如果你想给一个属性一个默认值以在没有指定值时返回， 你可以通过 '=' 和 "默认值"  放在参数后面来"使用解构赋值实现默认值"
                                         */
                                          function Avatar({person,size=100}){ // 对props对象结构赋值时，顺便给属性一个"默认值"
                                                // ...
                                          }
                                          /*
                                             现在，如果没有Size属性的情况下渲染<Avatar person={} /> ，则size将被设置为100.

                                             默认值仅在缺少size属性，或传递size={undefined} 时使用。 如果传递了 size={null} 或者 size={0} 则不会使用默认值。
                                          */
                                          
                                     
                                // 4、使用"JSX展开语法"转发属性
                                          // 有时候，"传递属性" 会变得非常重复：
                                              function Profile({person,size,isSepia,thickBorder}){
                                                return(
                                                    <Avatar
                                                        person={person}
                                                        size={size}
                                                        isSepia={isSepia}
                                                        thickBorder={thickBorder}
                                                    />
                                                );
                                              }

                                           /*
                                               虽然，重复的代码没有什么问题，它可以使逻辑更清晰。但有时候你可能会更看重简洁。
                                               一些组件将它们所有的属性转发给它们的子组件，就像这个Profile 对 Avatar 所做的那样。
                                               因为它们不直接使用任何属性，所以使用简洁的"展开语法" 是有意义的。
                                           */
                                               function Profile(props){
                                                   return (
                                                    <div className="card">
                                                        <Avatar {...props}/>
                                                    </div>
                                                   );
                                               }

                                               // 这会将 Profile 组件的所有属性转发个Avatar,但不会列出每个属性的名称。

                                // 5、将JSX作为"子组件"传递
                                           // 嵌套浏览器标签是很常见的：
                                               /*
                                                    <div>
                                                        <img/>
                                                    </div>
                                               */

                                            // 有时候你会希望以相同的方式"嵌套自己的组件":
                                                /*
                                                    <card>
                                                        <Avatar/>
                                                    </card>
                                                */
                                            
                                            /*
                                                当你将内容嵌套在JSX标签中时，父组件将在名为 children 的属性中"接收到该子组件的内容"。
                                                    
                                                例如，下面的Card组件将接收一个被设为<Avatar/>的 children prop 并将其包裹在 div中渲染：
                                            */ 
                                                    // utils.js文件：
                                                        export function getIamgeUrl(person,size='s'){
                                                            return (
                                                                'https://i.imgur.com/' +
                                                                person.imageId +
                                                                size +
                                                                '.jpg'
                                                            );
                                                        }
                                                    
                                                    // Avatar.js 文件：
                                                        import {getIamgeUrl} from './utils.js';

                                                        export default function Avatar({person,size}){
                                                            return (
                                                                <img 
                                                                   className="avatar"
                                                                   src={getIamgeUrl(person)}
                                                                   alt={person.name}
                                                                   width={size}
                                                                   height={size}
                                                                />
                                                            );
                                                        }

                                                    // app.js 文件：
                                                        import Avatar from './Avatar.js';

                                                        function Card({children}){  // 声明父组件
                                                            return (
                                                                <div>
                                                                    {children}  
                                                                </div>
                                                            );
                                                        }

                                                        export default function Profile(){ // 默认导出一个组价
                                                            return (
                                                                /*
                                                                     因为这里给<Card>内部嵌套了<Avatar>，就会把<Avatar>的JSX内容作为名为"children"的属性传递给<Card>。
                                                                    
                                                                     在<Card>中，通过解构赋值获取到children属性，决定了<Avatar>的JSX内容将要被渲染在<Card>的JSX内容的位置。
                                                                */
                                                                <Card> 
                                                                    <Avatar person={{
                                                                            name:'余冰',
                                                                            imagId:'55565'
                                                                        }}
                                                                        size ={100}
                                                                />
                                                                </Card>
                                                            );
                                                        }

                                            
                                            /*
                                                    尝试用一些文本调换<Card>中的<Avatar>组件，看看Card组件如何包裹任意嵌套内容。
                                                    
                                                    <Card>不必"知道"其中渲染的内容。 你会在很多地方看到这种灵活的模式。


                                                    可以将带有 children属性的组件"看作组件上有个洞"，
                                                    （在声明这个组件事件时，你定义了这个洞，并且你定义了这个洞在这个组件的哪个位置）。

                                                    然后这个带有children属性组件的"父组件"在使用这个组件时，可以使用任意JSX来填充。
                                                    (你会经常使用 children prop 来进行视觉包装： 面板、网格等等)
                                            */
                            

                                // Props(属性)如何随时间变化
                                        /*
                                            下面的 Clock组件时从其父组件接收两个 props: color 和 time .
                                            (父组件的代码被省略，因为它使用 state ,我们暂时不会深入研究)
                                        */
                                             // 尝试在下面的选择框中更改颜色
                                                  export default function Clock({color,time}){
                                                    return(
                                                        <h1 style={{color:color}}>
                                                            {time}
                                                        </h1>
                                                    );
                                                  }
                                            /*
                                                这个例子说明，"一个组件可能会随着时间的推移而收到不同的props属性对象"。

                                                Props(属性对象)并不总是静态的！
                                                在这里，time 属性每秒都在发生变化。当你选择另一种颜色时，color 属性也改变了。
                                                Props反映了组件"在任何时间点"的数据，并不仅仅是在开始时。

                                                然而，props是"不可变的"。 当一个组件需要改变它的 props时(例如，响应用户交互或新数据)时，
                                                它不得不"请求"它的父组件传递"不同的props"（一个新的props对象）。
                                                它的旧的props对象将被丢弃，最终JavaScript引擎将会回收它的内存。

                                                "不要尝试更改props"。
                                                当你需要响应用户输入时，你可以"设置state"，要知道"state是一个组件的内存"
                                            */


            // 五、条件渲染
                  /*
                       通常你的组件会需要根据不同的情况显示不同的内容。
                       在React中，你可以通过使用JavaScript的 "if" 语句、 "&&" 和 "? :" 运算符选择性的渲染JSX
                  */

                          // 1、条件返回JSX
                              /*
                                    假设有一个PackingList组件，里面渲染多个Item组件，每个物品可标记为打包与否：
                              */
                                    // app.js文件中：
                                          function Item({name,isPacked}){
                                                if (isPacked) {
                                                    return <li className="item">{name} ✔</li>;
                                                }
                                                return <li className="item">{name}</li>;
                                          }

                                          export default function PackingList() {
                                                return (
                                                    <section>
                                                    <h1>Sally Ride 的行李清单</h1>
                                                    <ul>
                                                        <Item 
                                                            isPacked={true} 
                                                            name="宇航服" 
                                                        />
                                                        <Item 
                                                            isPacked={true} 
                                                            name="带金箔的头盔" 
                                                        />
                                                        <Item 
                                                            isPacked={false} 
                                                            name="Tam 的照片" 
                                                        />
                                                    </ul>
                                                    </section>
                                                );
                                          }

                                    /*
                                        需要注意的是，有些item组件的 isPacked属性是被设为 true的。你可以字那些满足 isPacked==={true}条件的
                                        物品上加一个勾选符号"√"

                                        像上面的代码，如果isPacked属性是 true ,Item组件会返回"不一样的JSX"。

                                        留意这里你是怎么使用JavaScript的if 和return语句来写分支逻辑的。
                                        在React中，是由JavaScript来处理控制流的。
                                    */


                           // 2、选择性地返回null
                                     /*
                                          在一些情况下，你不想有任何东西进行渲染。
                                          但一个组件必须返回一些东西。这种情况下，你可以直接返回null.
                                     */
                                         // eg:
                                             function Item({name,isPacked}){
                                                if(isPacked){
                                                    return null;
                                                }
                                                return <li className="item">{name}</li>;
                                             }

                                         /*
                                             实际上，在组件里返回null并不常见，因为这样会让想使用它的开发者感觉奇怪。
                                             通常情况下，你可以"在父组件里决定"是否要渲染该组件。
                                         */

                            // 3、选择性地包含JSX
                                        /*
                                             在之前的例子里，你在组件内部控制那些JSX树会返回。
                                             你可能已经发现了字渲染输出里会有一些内容重复：
                                               
                                                <li className="item">{name} ✔</li>
                                            
                                              和下面的很像：
                                                
                                                <li className="item">{name} </li>
                                        */
                                         
                                        /*
                                            使用三元运算符:
                                        */

                                        function Item({ name, isPacked }) {
                                            return (
                                                <li className="item">
                                                    { isPacked ? (
                                                                    <del>
                                                                        {name + ' ✔'}
                                                                    </del>
                                                                 ) 
                                                                :
                                                                 ( name )
                                                    }
                                                </li>
                                            );
                                        }
        </script>
    </body>
</html>